#include "SimpleHistogram.h"


SimpleHistogram::SimpleHistogram(void)
{
	bins = NULL;
	colors = NULL;
	eachBinRg = NULL;
	pairs = NULL;
}


SimpleHistogram::~SimpleHistogram(void)
{
	if (pairs != NULL) delete[] pairs;
	if (eachBinRg != NULL) delete[] eachBinRg;
	if (bins != NULL) delete[] bins;
	if (colors != NULL) cvReleaseMat(&colors);
}

void SimpleHistogram::setRange(double a, double b) {
	ranges[0] = a;
	ranges[1] = b;
	if (bins != NULL)
		binW = (b - a) / binNum;
}
void SimpleHistogram::setBinNum(int n) {
	binNum = n;
	if (pairs != NULL) {
		delete[] pairs;
		pairs = NULL;
	}
	if (eachBinRg != NULL) {
		delete[] eachBinRg;
		eachBinRg = NULL;
	}
	if (bins != NULL) delete[] bins;
	bins = new int[n];
	memset(bins, 0, sizeof(int)*n);
	binW = (ranges[1]-ranges[0]) / binNum;
}

inline bool myGt(const pair<int, double> &x, const pair<int, double> &y) {
	return x.first > y.first;
}

void SimpleHistogram::sortBins(void) {
	if (pairs == NULL)
		pairs = new pair<int, double>[binNum];
	for (int i=0; i<binNum; ++i) {
		pairs[i].first = bins[i];
		pairs[i].second = i*binW;
	}
	sort(pairs, pairs+binNum, myGt);
	if (eachBinRg == NULL)
		eachBinRg = new double[binNum];
	for (int i=0; i<binNum; ++i) {
		bins[i] = pairs[i].first;
		eachBinRg[i] = pairs[i].second;
	}
}
void SimpleHistogram::accumulate(void) {
	for (int i=1; i<binNum; ++i)
		bins[i] += bins[i-1];
}

template<typename T>
void SimpleHistogram::calcHistFast(CvMat *mat) {
	memset(bins, 0, sizeof(int)*binNum);
	T *ptr;
	int idx;
	for (int i=0; i<mat->rows; ++i) {
		ptr = (T*)cvPtr2D(mat, i, 0);
		for (int j=0; j<mat->cols; ++j, ++ptr) {
			idx = cvFloor((*ptr-ranges[0])/binW);
			if (0 <= idx && idx < binNum)
				++bins[idx];
		}
	}
}

void SimpleHistogram::calcHistFast( CvMat *mat, short s )
{
	calcHistFast<short>(mat);
}


void SimpleHistogram::threshHist(double atLeast) {
	for (int i=0; i<binNum; ++i)
		if (bins[i] < atLeast) bins[i] = 0;
}

void SimpleHistogram::createColor(void) {
	if (colors != NULL)
		cvReleaseMat(&colors);
	colors = cvCreateMat(1, binNum, CV_8UC3);
	for( int i = 0; i < binNum; i++ ) {
		uchar *ptr = cvPtr2D(colors, 0, i);
		ptr[0] = uchar(i*180./binNum);
		ptr[1] = 255;
		ptr[2] = 255;
	}
	cvCvtColor(colors, colors, CV_HSV2BGR);
}

void SimpleHistogram::show(char *winName, int binW, int firstN) {
	if (winName == NULL) winName = "histogram";
	if (binW <= 0) binW = 20;
	if (firstN == -1) firstN = binNum;

	CvMat *histImg = cvCreateMat(200, binW*firstN, CV_8UC3);

	int maxBinVal = numeric_limits<int>::min();
	for (int i=0; i<firstN; ++i)
		if (bins[i] > maxBinVal) maxBinVal = bins[i];

	if (maxBinVal == 0) maxBinVal = 200;

	if (colors == NULL || firstN >= colors->cols) createColor();

	CvFont font;
	cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.4, 0.4);
	char words[100];

	for( int i = 0; i < firstN; i++ ) {
		int val = bins[i] * histImg->rows /maxBinVal;
		cvRectangle( histImg, cvPoint(i*binW, histImg->rows),
			cvPoint((i+1)*binW, histImg->rows - val),
			cvGet2D(colors, 0, i), -1, 8 );
		sprintf_s(words, 100, "%d", i);
		if (binW > 18)
			cvPutText(histImg, words, cvPoint(i*binW, histImg->rows-20), &font, cvScalar(255,255,255));
	}
	sprintf_s(words, "maxV: %d, val/pixel: %f.", maxBinVal, float(maxBinVal)/histImg->rows);
	cvPutText(histImg, words, cvPoint(20,10), &font, cvScalar(255,255,255));
	cvShowImage(winName, histImg);

	cvReleaseMat(&histImg);
}

double SimpleHistogram::mainMean(double perc) {
	int *bakBins = new int[binNum];
	memcpy(bakBins, bins, binNum*sizeof(int));
	sortBins();
	accumulate();

	double mean = 0;
	int cnt = 0;
	for (int i=0; i<binNum; ++i)
		if (bins[i] <= perc * bins[binNum-1]) {
			mean += bins[i] * (eachBinRg[i]+binW/2);
			cnt += bins[i];
		}
		else {
			mean /= cnt;
			break;
		}

		memcpy(bins, bakBins, binNum*sizeof(int));
		delete[] bakBins;
		return mean;
}

void SimpleHistogram::derive(void) {
	for (int i=0; i<binNum-1; ++i) {
		bins[i] = abs(bins[i+1] - bins[i]);
	}
	bins[binNum-1] = 0;
}



